{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Edit Knowledge Graphs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\">\n",
    "    <ul class=\"toc-item\">\n",
    "        <li><span><a href=\"#Introduction\" data-toc-modified-id=\"Introduction-1\">Introduction</a></span></li>\n",
    "        <li><span><a href=\"#Data-Editing\" data-toc-modified-id=\"Data-Editing-2\">Data Editing</a></span>\n",
    "            <ul>\n",
    "                <li><span><a href=\"#Adds\" data-toc-modified-id=\"Adds-2.1\">Adds</a></span></li>\n",
    "                <li><span><a href=\"#Updates\" data-toc-modified-id=\"Updates-2.2\">Updates</a></span></li>\n",
    "                <li><span><a href=\"#Deletes\" data-toc-modified-id=\"Deletes-2.3\">Deletes</a></span></li>\n",
    "            </ul>\n",
    "        </li>\n",
    "        <li><span><a href=\"#Data-Model-Editing\" data-toc-modified-id=\"Data-Model-Editing-3\">Data Model Editing</a></span>\n",
    "            <ul>\n",
    "                <li><span><a href=\"#Types\" data-toc-modified-id=\"Types-3.1\">Types</a></span>\n",
    "                    <ul>\n",
    "                        <li><span><a href=\"#Adds\" data-toc-modified-id=\"Adds-3.1.1\">Adds</a></span></li>\n",
    "                        <li><span><a href=\"#Updates\" data-toc-modified-id=\"Updates-3.1.2\">Updates</a></span></li>\n",
    "                        <li><span><a href=\"#Deletes\" data-toc-modified-id=\"Deletes-3.1.3\">Deletes</a></span></li>\n",
    "                    </ul>\n",
    "                </li>\n",
    "                <li><span><a href=\"#Properties\" data-toc-modified-id=\"Properties-3.2\">Properties</a></span></li>\n",
    "                    <ul>\n",
    "                        <li><span><a href=\"#Adds\" data-toc-modified-id=\"Adds-3.2.1\">Adds</a></span></li>\n",
    "                        <li><span><a href=\"#Updates\" data-toc-modified-id=\"Updates-3.2.2\">Updates</a></span></li>\n",
    "                        <li><span><a href=\"#Deletes\" data-toc-modified-id=\"Deletes-3.2.3\">Deletes</a></span></li>\n",
    "                    </ul>\n",
    "                </li>\n",
    "                <li><span><a href=\"#Search-Indexes\" data-toc-modified-id=\"Search-Indexes-3.3\">Search Indexes</a></span></li>\n",
    "            </ul>\n",
    "        </li>\n",
    "    </ul>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "The ArcGIS API for Python allows editing of both the data and data model of the [`KnowledgeGraph`](https://developers.arcgis.com/python/latest/api-reference/arcgis.graph.html#knowledgegraph). The goal of this guide is to provide information and examples of how to edit your knowledge graphs using python.\n",
    "\n",
    "Editing the data will allow for:\n",
    "- Creating and deleting entities\n",
    "- Creating and deleting relationships\n",
    "- Updating property values\n",
    "\n",
    "Editing the data model will allow for:\n",
    "- Creating and deleting entity types\n",
    "- Creating and deleting relationship types\n",
    "- Creating and deleting properties"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Editing\n",
    "All data editing is accomplished using [`knowledge_graph.apply_edits()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.apply_edits)\n",
    "### Adds\n",
    "\n",
    "To add an entity to the knowledge graph, we define the type and properties of the entity:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add_entity = Entity(\n",
    "    type_name=\"Company\", \n",
    "    properties={\n",
    "        \"name\": \"Esri\",\n",
    "        \"year_established\": 1969\n",
    "    }\n",
    ")\n",
    "\n",
    "knowledge_graph.apply_edits(adds=[add_entity], as_dict=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To add a relationship to the knowledge graph we must know which two entities we want to create the relationship between and use their ids along with information about the relationship (in the form of properties) to create it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add_relationship = Relationship(\n",
    "    type_name=\"WorksFor\", \n",
    "    origin_entity_id=\"{56BCCAF0-DD03-487A-BE39-F32164714190}\",\n",
    "    destination_entity_id=\"{26BCCAF0-DD03-484A-BE39-B32164718190}\",\n",
    "    properties={\n",
    "        \"startDate\": datetime(2020, 1, 6)\n",
    "    }\n",
    ")\n",
    "\n",
    "knowledge_graph.apply_edits(adds=[add_relationship], as_dict=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Updates\n",
    "\n",
    "To update the value of a property on an entity, provide the id of the entity along with the properties and values that you would like updated. This can include geometries for spatial entities:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "update_entity = Entity(\n",
    "    type_name=\"Company\", \n",
    "    id=\"{26BCCAF0-DD03-484A-BE39-B32164718190}\",\n",
    "    properties={\n",
    "        \"name\": \"Not Esri\",\n",
    "        \"shape\": Point(\n",
    "            {\n",
    "                \"x\":-89.86140978600001, \n",
    "                \"y\":38.902491682000004,\n",
    "                \"spatialReference\": {\"wkid\": 4326}\n",
    "            }\n",
    "        )\n",
    "    }\n",
    ")\n",
    "\n",
    "knowledge_graph.apply_edits(updates=[update_entity], as_dict=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deletes\n",
    "\n",
    "To delete instances of a type, the ids to be deleted need to be defined with the name of the type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "delete_entity = EntityDelete(\n",
    "    type_name=\"Company\", \n",
    "    ids=[\"{26BCCAF0-DD03-484A-BE39-B32164718190}\"]\n",
    ")\n",
    "\n",
    "knowledge_graph.apply_edits(deletes=[delete_entity], cascade_delete=True, as_dict=False) # be sure to set cascade_delete to True to automatically delete relationships attached to the provided entity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Model Editing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Types"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Adds\n",
    "\n",
    "Adding new types involves forming the type objects and pushing them into the data model of the knowledge graph using [`knowledge_graph.named_object_type_adds()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.named_object_type_adds). Named types (entity and relationship types) can be created in bulk or individually as needed. The following example shows creating these named types all at the same time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create the data model\n",
    "knowledge_graph.named_object_type_adds(\n",
    "    entity_types=[\n",
    "        EntityType(name=\"User\", properties={\"name\": GraphProperty(name=\"name\")}),\n",
    "        EntityType(name=\"Item\", properties={\n",
    "            \"id\": GraphProperty(name=\"id\"),\n",
    "            \"title\": GraphProperty(name=\"title\"),\n",
    "            \"type\": GraphProperty(name=\"type\")\n",
    "        })\n",
    "    ], \n",
    "    relationship_types=[\n",
    "        RelationshipType(name=\"DependsOn\"),\n",
    "        RelationshipType(name=\"Owns\")\n",
    "    ],\n",
    "    as_dict=False\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Updates\n",
    "\n",
    "Updates are performed on named object types using [`knowledge_graph.named_object_type_update()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.named_object_type_update). Updating the name of a type is not allowed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "knowledge_graph.named_object_type_update(\n",
    "    type_name=\"User\", \n",
    "    named_type_update=EntityType(name=\"User\", alias=\"NewUser\", strict=False), \n",
    "    mask=NamedObjectTypeMask(\n",
    "        update_alias=True,\n",
    "        update_strict=True\n",
    "    ),\n",
    "    as_dict=False\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Deletes\n",
    "\n",
    "Deleting types is as simple as passing the string name of the type to [`knowledge_graph.named_object_type_delete()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.named_object_type_delete)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "knowledge_graph.named_object_type_delete(type_name=\"User\", as_dict=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Properties"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Adds\n",
    "\n",
    "Properties can be added to named types at any point using [`knowledge_graph.graph_property_adds()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.graph_property_adds). Many properties can be added to a given type in a single call.\n",
    "\n",
    "The default type for a property is esriFieldTypeString, to define a different type use one of the [available field types](https://github.com/Esri/knowledge-pbf/blob/master/proto/esriPBuffer/EsriExtendedTypes/EsriExtendedTypes.proto):\n",
    "- esriFieldTypeSmallInteger\n",
    "- esriFieldTypeBigInteger\n",
    "- esriFieldTypeInteger\n",
    "- esriFieldTypeSingle\n",
    "- esriFieldTypeDouble\n",
    "- esriFieldTypeString\n",
    "- esriFieldTypeDate\n",
    "- esriFieldTypeDateOnly\n",
    "- esriFieldTypeTimeOnly\n",
    "- esriFieldTypeTimestampOffset\n",
    "- esriFieldTypeOID\n",
    "- esriFieldTypeGeometry\n",
    "    - with this type, you also need to [define geometryType](https://github.com/Esri/knowledge-pbf/blob/master/proto/esriPBuffer/EsriTypes.proto):\n",
    "        - esriGeometryPoint\n",
    "        - esriGeometryPolyline\n",
    "        - esriGeometryPolygon\n",
    "        - esriGeometryMultipoint\n",
    "- esriFieldTypeGUID\n",
    "- esriFieldTypeGlobalID\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "knowledge_graph.graph_property_adds(\n",
    "    type_name=\"User\", \n",
    "    graph_properties=[\n",
    "        GraphProperty(name=\"name\"),\n",
    "        Graphproperty(name=\"post_count\", field_type=\"esriFieldTypeInteger\"),\n",
    "        GraphProperty(name=\"shape\", field_type=\"esriFieldTypeGeometry\", geometry_type=\"esriGeometryPoint\")\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Updates\n",
    "\n",
    "Information about the properties on a type can be updated using [`knowledge_graph.graph_property_update()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.graph_property_update). Only one property of a given type can be updated in a single call."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "knowledge_graph.graph_property_update(\n",
    "    type_name=\"User\", \n",
    "    property_name=\"name\",\n",
    "    graph_property=GraphProperty(name=\"name\", alias=\"user_name\", field_type=\"esriFieldTypeString\"),\n",
    "    mask=GraphPropertyMask(\n",
    "        update_alias=True,\n",
    "        update_field_type=True\n",
    "    ),\n",
    "    as_dict=False\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Deletes\n",
    "\n",
    "Deleting a property is a very simple, just pass the name of the type and property to be deleted using [`knowledge_graph.graph_property_delete()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.graph_property_delete). Only one property from any given type can be deleted in a single call. To delete multiple properties, it may be good to loop through a list of properties you plan to delete or make multiple calls consecutively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph.graph_property_delete(type_name=\"User\", property_name=\"name\", as_dict=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Search Indexes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`esriFieldTypeString` properties can be added to the [`SearchIndex`](https://developers.arcgis.com/python/latest/api-reference/arcgis.graph.html#searchindex) to allow the contents of those properties to be searched for when using [`knowledge_graph.search()`](https://developers.arcgis.com/python/api-reference/arcgis.graph.html#arcgis.graph.KnowledgeGraph.search)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# add search indexes to individual properties\n",
    "knowledge_graph.update_search_index(adds={\"User\": SearchIndexProperties(property_names=[\"name\"])}, as_dict=False)\n",
    "\n",
    "# add search indexes to all text properties based on the data model\n",
    "datamodel = knowledge_graph.query_data_model()\n",
    "for entity_type in datamodel.entity_types:\n",
    "    prop_list = []\n",
    "    for prop in datamodel.entity_types[entity_type].properties:\n",
    "        if datamodel.entity_types[entity_type].properties[prop].fieldType == 'esriFieldTypeString':\n",
    "            prop_list.append(prop)\n",
    "    knowledge_graph.update_search_index(adds={entity_type: SearchIndexProperties(property_names=prop_list)}, as_dict=False)\n",
    "for entity_type in datamodel.relationship_types:\n",
    "    prop_list = []\n",
    "    for prop in datamodel.relationship_types[entity_type].properties:\n",
    "        if datamodel.relationship_types[entity_type].properties[prop].fieldType == 'esriFieldTypeString':\n",
    "            prop_list.append(prop)\n",
    "    knowledge_graph.update_search_index(adds={entity_type: SearchIndexProperties(property_names=prop_list)}, as_dict=False)\n",
    "\n",
    "# delete a property from the search index\n",
    "knowledge_graph.update_search_index(deletes={\"User\": SearchIndexProperties(property_names=[\"name\"])}, as_dict=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  },
  "vscode": {
   "interpreter": {
    "hash": "d622b5871f1605057390dea3c8b45e995d0d19bef8604acd7f5b2e1066a85139"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
